package me.macd.mindoc.util;

import me.macd.mindoc.annotation.Table;
import org.apache.commons.beanutils.PropertyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

public class SqlUtil {
    private static final Logger log = LoggerFactory.getLogger(SqlUtil.class);

    public static SqlContext buildInsertSql(Object entity, NameHandler nameHandler) {
        Class<?> clazz = entity.getClass();

        // 获取实体对应表名
        Table table = clazz.getAnnotation(Table.class);
        if (table == null) {
            throw new RuntimeException("no annotation named @Table");
        }
        StringBuilder sql = new StringBuilder("insert into ");
        sql.append(table.tableName()).append(" (");

        PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(entity);

        List<Object> params = new ArrayList<>();

        StringBuilder args = new StringBuilder("(");

        for (PropertyDescriptor pd : descriptors) {
            Object value = getReadMethodValue(pd.getReadMethod(), entity);
            if (value == null) {
                continue;
            }
            sql.append(nameHandler.columnName(pd.getName()));
            args.append("?");
            params.add(value);
            sql.append(",");
            args.append(",");
        }
        sql.deleteCharAt(sql.length() - 1);
        args.deleteCharAt(args.length() - 1);
        args.append(")");
        sql.append(")");
        sql.append(" values ").append(args);

        return new SqlContext(sql, table.primaryKey(), params);
    }

    public static SqlContext buildUpdateSql(Object entity, NameHandler nameHandler) {
        Class<?> clazz = entity.getClass();

        // 获取实体对应表名
        Table table = clazz.getAnnotation(Table.class);
        if (table == null) {
            throw new RuntimeException("no annotation named @Table");
        }

        StringBuilder sql = new StringBuilder("update ");
        sql.append(table.tableName()).append(" set ");

        PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(entity);

        List<Object> params = new ArrayList<>();

        Object keyValue = null;
        for (PropertyDescriptor pd : descriptors) {
            Object value = getReadMethodValue(pd.getReadMethod(), entity);
            if (value == null) {
                continue;
            }
            String columnName = nameHandler.columnName(pd.getName());
            if (table.primaryKey().equals(columnName)) {
                keyValue = value;
                continue;
            }
            sql.append(columnName).append("=?");
            params.add(value);
            sql.append(",");
        }
        sql.deleteCharAt(sql.length() - 1);
        sql.append(" where ").append(table.primaryKey()).append("=?");
        params.add(keyValue);

        return new SqlContext(sql, table.primaryKey(), params);
    }

    /**
     * 获取属性值
     *
     * @param readMethod
     * @param entity
     * @return
     */
    private static Object getReadMethodValue(Method readMethod, Object entity) {
        if (readMethod == null) {
            return null;
        }
        try {
            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                readMethod.setAccessible(true);
            }
            return readMethod.invoke(entity);
        } catch (Exception e) {
            throw new RuntimeException("获取属性值失败", e);
        }
    }

    public static class SqlContext {
        private String primaryKey;
        private StringBuilder sql;
        private List<Object> params;

        public SqlContext(StringBuilder sql, String primaryKey, List<Object> params) {
            this.sql = sql;
            this.params = params;
            this.primaryKey = primaryKey;
        }

        public StringBuilder getSql() {
            return sql;
        }

        public void setSql(StringBuilder sql) {
            this.sql = sql;
        }

        public List<Object> getParams() {
            return params;
        }

        public void setParams(List<Object> params) {
            this.params = params;
        }

        public String getPrimaryKey() {
            return primaryKey;
        }

        public void setPrimaryKey(String primaryKey) {
            this.primaryKey = primaryKey;
        }
    }
}
